1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.data.ini; 12 public import hip.api.data.ini; 13 import hip.api.data.asset; 14 import hip.util.conv:to; 15 16 17 class HipINI : HipAsset, IHipIniFile 18 { 19 IniBlock[string] _blocks; 20 string path; 21 bool configFound = false; 22 bool _noError = true; 23 string[] errors; 24 25 bool noError() const{return _noError;} 26 ref IniBlock[string] blocks(){return _blocks;} 27 const(string[]) getErrors() const{return cast(const)errors;} 28 29 this() 30 { 31 super("HipINI"); 32 _typeID = assetTypeID!HipINI; 33 } 34 35 bool loadFromMemory(string data, string path) 36 { 37 this.path = path; 38 if(data != "") 39 configFound = true; 40 41 for(int i = 0; i < data.length; i++) 42 { 43 char c = data[i]; 44 if(c == ';' || c == '#') 45 { 46 while(i < data.length && data[++i] != '\n'){} 47 continue; 48 } 49 else if(c == '[') 50 { 51 import hip.util.string : replaceAll, trim, splitRange; 52 import hip.util.algorithm:put; 53 int captureStart = i+1; 54 while(i < data.length) 55 { 56 i++; 57 if(i >= data.length) 58 return true; 59 else if(data[i] == ']') 60 break; 61 } 62 63 IniBlock block; 64 block.name = data[captureStart..i]; 65 string capture = ""; 66 captureStart = i+1; 67 //Read until finding a key. 68 while(++i < data.length && data[i] != '['){} 69 capture = data[captureStart..i]; 70 71 72 foreach(l; capture.splitRange("\n")) 73 { 74 l = l.trim; 75 if(l == "") 76 continue; 77 string k, v; 78 l.splitRange("=").put(&k, &v); 79 if(v == "") 80 { 81 errors~= "No value for key '"~k~"'"; 82 _noError = false; 83 break; 84 } 85 string name = k.replaceAll(' ', ""); 86 block.vars[name] = IniVar(name, formatValue(v)); 87 } 88 blocks[block.name] = block; 89 i--; 90 } 91 } 92 return true; 93 } 94 95 /** 96 * Simple parser for the .conf or .ini files commonly found. 97 */ 98 static HipINI parse(string content, string path = "") 99 { 100 HipINI f = new HipINI(); 101 if(!f.loadFromMemory(content, path)) 102 return null; 103 return f; 104 } 105 106 IniVar* getIniVar(string varPath) 107 { 108 import hip.util.string:splitRange; 109 import hip.util.algorithm; 110 string accessorA, accessorB; 111 varPath.splitRange(".").put(&accessorA, &accessorB); 112 if(accessorB == "") 113 return null; 114 IniBlock* b = (accessorA in blocks); 115 if(b is null) 116 return null; 117 return (accessorB in *b); 118 } 119 120 override void onFinishLoading(){} 121 override void onDispose(){} 122 override bool isReady() const {return errors.length == 0;} 123 124 } 125 126 /** 127 * Remove comments and spaces from the Value from KeyValue pair 128 */ 129 private string formatValue(string unformattedValue) 130 { 131 string ret; 132 foreach(ch; unformattedValue) 133 { 134 if(ch == '#' || ch == ';') //Remove comments 135 break; 136 if(ch == ' ') //Remove spaces for to!int and friends working correctly 137 continue; 138 ret~= ch; 139 } 140 return ret; 141 }